

from lib import Util;
from src.gadgets.servlet import *;
class DataRequestHandler:
	def __init__(request, response):
		self.request = request;
		self.response = response;
	def handleMethod(self, requestItem):
		token = requestItem.getToken();
		method = requestItem.getMethod();
		if (token.isAnonymous()  and  method != 'GET'):
			# Anonymous requests are only allowed to GET data (not create/edit/delete)
			response = ResponseItem(BAD_REQUEST, "[method] not allowed for anonymous users", None);
		elif (method == 'GET'):
			parameters = requestItem.getParameters();
			if ("@supportedFields" in parameters):
				response = self.getSupportedFields(parameters);
			else:
				response = self.handleGet(requestItem);
			
		elif (method == 'POST'):
			response = self.handlePost(requestItem);
		elif (method == 'DELETE'):
			response = self.handleDelete(requestItem);
		elif (method == 'PUT'):
			response = self.handlePut(requestItem);
		else:
			response = ResponseItem(BAD_REQUEST, "Unserviced Http method type", None);
		
		return response;
	
	@staticmethod
	def getAppId(appId, token):
		if (appId == '@app'):
			return token.getAppId();
		else:
			return appId;
	
	@staticmethod	
	def convertToObject(string):
		#TODO should detect if it's atom/xml or json here really. assuming json for now
		from lib import Util;
		decoded = Util.json_decode(string);
		if (decoded == string):
			raise Exception,"Invalid JSON syntax";
		
		return decoded;
	

	# 
	# To support people/@supportedFields and activity/@supportedFields 
	# @param parameters url parameters to get request type(people/activity)
	# 
	def getSupportedFields(self, parameters):
		context = GadgetContext(self.request, self.response, 'GADGET');
		container = context.getContainer();
		from lib import Config;
		containerConfig = ContainerConfig(Config.get('container_path'));
		config = containerConfig.getConfig(container, 'gadgets.features');
		version = self.getOpenSocialVersion(config);
		supportedFields = config[version]['supportedFields'];
		if ('people' in parameters):
			ret = supportedFields['person'];
		else:
			ret = supportedFields['activity'];
		
		return ResponseItem(None, None, ret);
	

	# 
	# To get OpenSocial version fro getting supportedFields 
	# @param config configuration values from container's js files
	# 
	def __getOpenSocialVersion_(self, config):
		str1 = "opensocial-";
		version = [];
		for key in config:
			value = config[key];
			if (str1[0:len(key)] == str1):
				version.append(key);

		if (len(version) == 0):
			raise Exception, "Invalid container configuration, opensocial-x.y key not found";
		
		#[todo]
		#rsort(version);
		return version[0];
	

	def handleDelete(self, requestItem):
		from lib import Util;
		Util.abstract();

	def handleGet(self, requestItem):
		from lib import Util;
		Util.abstract();

	def handlePost(self, requestItem):
		from lib import Util;
		Util.abstract();

	def handlePut(self, requestItem):
		from lib import Util;
		Util.abstract();






class ActivitiesHandler (DataRequestHandler):
	__ACTIVITY_ID_PATH_ = "/activities/{userId}/{groupId}/{activityId}";

	def __init__(self):
		from lib import Config;
		service = Config.get('activity_service');
		self.__service_ = service();
	

	def handleDelete(self, requestItem):
		raise ResponseItem(BAD_REQUEST, "You can't delete activities. ", None);
	

	# /activities/(userId}/(groupId}/(optionalActvityId}
	# examples:
	# /activities/john.doe/@self/1
	# /activities/john.doe/@self
	# /activities/john.doe/@friends
	
	def handleGet(self, requestItem):
		requestItem.parseUrlWithTemplate(self.__ACTIVITY_ID_PATH_);
		parameters = requestItem.getParameters();
		optionalActivityId = parameters['activityId'] if ('activityId' in parameters) else None;
		# TODO: Filter by fields
		# TODO: do we need to add pagination and sorting support?
		if (optionalActivityId != None):
			return self.__service_.getActivity(requestItem.getUser(), requestItem.getGroup(), optionalActivityId, requestItem.getStartIndex(), requestItem.getCount(), requestItem.getToken());
		
		ret = self.__service_.getActivities(requestItem.getUser(), requestItem.getGroup(), requestItem.getStartIndex(), requestItem.getCount(), requestItem.getToken());
		if (ret.getError()):
			raise ResponseItem(None, None, array());
		
		return ret;

	# /activities/(userId}/@self
	# examples:
	# /activities/@viewer/@self/@app
	# /activities/john.doe/@self
	# - postBody is an activity object

	def handlePost(self, requestItem):
		requestItem.parseUrlWithTemplate(self.__ACTIVITY_ID_PATH_);
		return self.__service_.createActivity(requestItem.getUser(), requestItem.getPostData(), requestItem.getToken());
	

	# /activities/(userId}/@self
	# examples:
	# /activities/john.doe/@self
	# - postBody is an activity object
	
	def handlePut(self, requestItem):
		return this.handlePost(requestItem);
	

class MessagesHandler(DataRequestHandler):
	__MESSAGES_PATH_ = "/messages/{userId}/outbox/{msgId}";

	def __init__(self):
		from lib import Config;
		service = Config.get('messages_service');
		self.__service_ = service();

	def handleDelete(self, requestItem):
		return ResponseItem(NOT_IMPLEMENTED, "You can't delete messages", None);
	

	def handleGet(self, requestItem):
		return ResponseItem(NOT_IMPLEMENTED, "You can't retrieve messages", None);
	

	def handlePost(self, requestItem):
		return ResponseItem(NOT_IMPLEMENTED, "You can't edit messages", None);
	

	# 
	# messages/(groupId}/outbox/(msgId}
	# 
	# @param RestRequestItem requestItem
	# @return responseItem
	# 
	def handlePut(self, requestItem):
		requestItem.parseUrlWithTemplate(MessagesHandler.__MESSAGES_PATH_);
		return self.service.createMessage(requestItem.getUser(), requestItem.getPostData(), requestItem.getToken());
	


class PeopleHandler(DataRequestHandler):
	
	__PEOPLE_PATH_ = "/people/{userId}/{groupId}/{personId}";
	_DEFAULT_PERSON_FIELDS = {"id" : 1, "name" : 1, "thumbnailUrl" : 1};

	def __init__(self):
		from lib import Config;
		service = Config.get('people_service');
		self.__service_ = service();

	# 
	# people/(userId}/(groupId}/(optionalPersonId}
	# 
	# examples:
	# people/john.doe/@all
	# people/john.doe/@friends
	# people/john.doe/@self
	# 
	def handleGet(self, requestItem):
		requestItem.parseUrlWithTemplate(PeopleHandler.__PEOPLE_PATH_);
		parameters = requestItem.getParameters();
		optionalPersonId = parameters['personId'] if parameters.has_key('personId') else None;
		fields = requestItem.getFieldsWithDefaultValue(PeopleHandler._DEFAULT_PERSON_FIELDS);
		if (optionalPersonId):
			return self.__service_.getPerson(requestItem.getUser(), optionalPersonId, fields, requestItem.getToken());
		else : 
			if (requestItem.getGroup().getType() == 'self'):
				return self.__service_.getPerson(requestItem.getUser(), requestItem.getGroup(), fields, requestItem.getToken());	
		startIndex = requestItem.getStartIndex();		
		count = requestItem.getCount();		
		networkDistance = requestItem.getNetworkDistance();
		if ((not Util.is_numeric(startIndex)) and (not Util.is_numeric(count)) and (not Util.is_numeric(networkDistance))):
			return ResponseItem(BAD_REQUEST, "Invalid options specified", None);
		else:
			from src.social.spi import CollectionOptions;
			options = CollectionOptions();
			options.setSortBy(requestItem.getSortBy());
			options.setSortOrder(requestItem.getSortOrder());
			options.setFilterBy(requestItem.getFilterBy());
			options.setFilterOperation(requestItem.getFilterOperation());
			options.setFilterValue(requestItem.getFilterValue());
			options.setUpdatedSince(requestItem.getUpdatedSince());
			options.setStartIndex(startIndex);
			options.setCount(count);
			options.setNetworkDistance(networkDistance);
			return self.__service_.getPeople(requestItem.getUser(), requestItem.getGroup(), options, fields, requestItem.getToken());
		
	def handleDelete(self, requestItem):
		return ResponseItem(BAD_REQUEST, "You can't delete people.", None);

	def handlePost(self, requestItem):
		return ResponseItem(NOT_IMPLEMENTED, "You can't edit people right now.", None);
	

	def handlePut(self, requestItem):
		return ResponseItem(NOT_IMPLEMENTED, "You can't add people right now.", None);
	


# 
# Represents the response items that get handed back as json within the
# DataResponse
# 
class ResponseItem:
	
	def __init__(self, error = None, errorMessage = None, response = None):
		self.error = error;
		self.errorMessage = errorMessage;
		self.response = self.__trimResponse_(response);
		if (self.error == None  and  self.errorMessage == None):
			# trim None values of self too
			del self.error;
			del self.errorMessage;
		
	# 
	# the json_encode function does not trim None values,
	# so we do this manually
	# 
	# @param mixed object
	# 
	def __trimResponse_(self, object):
		from lib import Util;
		if (Util.is_array(object)):
			if(type(object).__name__ == 'dict'):
				keys = object.keys();
			else:
				keys = range(len(object));

			for key in keys:
				val = object[key];
				# binary compare, otherwise False == 0 == None too
				if (val == None):
					del(object[key]);
				elif (Util.is_array(val)):
					object[key] = self.__trimResponse_(val);
				
		else :	
			if object != None : 
				vars = {};			
				vars = object.__dict__ ;
				keys = vars.keys();
				for key in keys:
					val = vars[key];
					if (val == None):
						del(object.key);
					elif (Util.is_array(val)):
						object.key = self.__trimResponse_(val);
				

		return object;

	def getError(self):
		if "error" in (self.__dict__).keys() :
			return self.error if self.error else None;
	
	def setError(self, error):
		self.error = error;
	

	def getErrorMessage(self):
		if "errorMessage" in (self.__dict__).keys() : 
			return self.errorMessage;

	def setErrorMessage(self, errorMessage):
		self.errorMessage = errorMessage;
	

	def getResponse(self):
		return self.response;
	
	def setResponse(self, response):
		self.response = response;

class AppDataHandler(DataRequestHandler):
	__APP_DATA_PATH = "/people/{userId}/{groupId}/{appId}";

	def __init__(self):
		from lib import Config;
		service = Config.get('app_data_service');
		self.__service_ = service();
	

	# 
	# people/(userId}/(groupId}/(appId}
	# - fields=(field1, field2}
	# 
	# examples:
	# appdata/john.doe/@friends/app?fields=count
	# appdata/john.doe/@self/app
	# 
	# 
	def handleDelete(self, requestItem):
		requestItem.parseUrlWithTemplate(AppDataHandler.__APP_DATA_PATH);
		return self.__service_.deletePersonData(requestItem.getUser(), requestItem.getGroup(), requestItem.getFields(), requestItem.getAppId(), requestItem.getToken());
	

	# 
	# appdata/(userId}/(groupId}/(appId}
	# - fields=(field1, field2)
	# 
	# examples:
	# appdata/john.doe/@friends/app?fields=count
	# appdata/john.doe/@self/app
	# 
	def handleGet(self, requestItem):
		requestItem.parseUrlWithTemplate(AppDataHandler.__APP_DATA_PATH);
		return self.__service_.getPersonData(requestItem.getUser(), requestItem.getGroup(), requestItem.getFields(), requestItem.getAppId(), requestItem.getToken());
	

	# 
	# people/(userId)/(groupId)/(appId)
	# - fields=(field1, field2)
	# 
	# examples:
	# appdata/john.doe/@friends/app?fields=count
	# appdata/john.doe/@self/app
	# 
	# The post data should be a regular json object. All of the fields vars will
	# be pulled from the values and set on the person object. If there are no
	# fields vars then all of the data will be overridden.
	# 
	def handlePost(self, requestItem):
		requestItem.parseUrlWithTemplate(AppDataHandler.__APP_DATA_PATH);
		# if no ?fields=foo,bar was specified, we try to guess them from the post data
		postFields = [];
		if (requestItem.getPostData() != None):
			data = requestItem.getPostData();
			keys = data.keys();
			for key in keys:
				postFields.append(key);
			
		return self.__service_.updatePersonData(requestItem.getUser(), requestItem.getGroup(), requestItem.getFieldsWithDefaultValue(postFields), requestItem.getPostData(), requestItem.getAppId(), requestItem.getToken());

	# 
	# people/(userId)/(groupId)/(appId)
	# - fields=(field1, field2)
	# 
	# examples:
	# appdata/john.doe/@friends/app?fields=count
	# appdata/john.doe/@self/app
	# 
	# The post data should be a regular json object. All of the fields vars will
	# be pulled from the values and set on the person object. If there are no
	# fields vars then all of the data will be overridden.
	# 
	def handlePut(self, requestItem):
		return self.handlePost(requestItem);
	


# 
# Represents the request items that come from the restful request.
# 
class RestRequestItem:
	# Common OpenSocial RESTful fields
	APP_ID = "appId";
	USER_ID = "userId";
	GROUP_ID = "groupId";
	START_INDEX = "startIndex";
	COUNT = "count";
	SORT_BY = "orderBy";
	SORT_ORDER = "sortOrder";
	NETWORK_DISTANCE = "networkDistance";
	FILTER_BY = "filterBy";
	FILTER_OP = "filterOp";
	FILTER_VALUE = "filterValue";
	UPDATED_SINCE = "updatedSince";
	FIELDS = "fields";
	
	# OpenSocial defaults
	DEFAULT_START_INDEX = 0;
	DEFAULT_COUNT = 20;
	DEFAULT_SORT_ORDER = 'ascending';
	
	APP_SUBSTITUTION_TOKEN = "@app";

	def __init__(self):
		
		self.__url_ = "";
		self.__method_ = "";
		self.__parameters_ = {};
		self.__token_ = "";
		self.__postData_ = "";

	def createRequestItemWithRequest(self, request, token):
		self.__url_ = request['url'];
		self.__parameters_ = self.__createParameterMap_(request['url']);
		self.__token_ = token;
		self.__method_ = request['method'];
		if (request.has_key('postData')):
			self.__postData_ = request['postData'];
		
	def createRequestItem(self, url, token, method, params, postData):
		self.__url_ = url;
		self.__parameters_ = params;
		self.__token_ = token;
		self.__method_ = method;
		self.__postData_ = postData;
	

	def __createParameterMap_(self, uri):
		from lib import Util, Config;
		from lib import RequestUtil;
		requestUtilHandler = RequestUtil.getInstance();	

		# get the rest params
		if (uri == None):
			uri = Util.substr(requestUtilHandler.headers["REQUEST_URI"], len(Config.get('web_prefix') + '/social/rest') + 1);
		else:
			uri = Util.substr(uri, 1);

		pos = uri.find('?');

		if (pos != -1):
			uri = Util.substr(uri, 0, pos);
		paramMap = {};
		params = uri.split('/')
		for i in range(len(params)) :
			paramMap[i] = params[i];		
		return paramMap;

	
	# Takes any url params out of the url and puts them into the param map.
	# Usually the servlet request code does this for us but the batch request calls have to do it
	# by hand.
	# 
	def __putUrlParamsIntoParameters_(self):
		from lib import Util;
		import urllib;
		fullUrl = self.__url_;
		queryParamIndex = fullUrl.find("?");
		if (queryParamIndex > 0):
			self.__url_ = Util.substr(fullUrl, 0, queryParamIndex);
			queryParams = Util.substr(fullUrl, queryParamIndex + 1);
			params = queryParams.split("&");			
			for param in params:
				paramPieces = param.split("=", 2);
				self.__parameters_[paramPieces[0]] = paramPieces[1] if len(paramPieces) == 2  else "";
			

	# 
	# This could definitely be cleaner..
	# TODO: Come up with a cleaner way to handle all of this code.
	# 
	# @param urlTemplate The template the url follows
	# 
	def parseUrlWithTemplate(self, urlTemplate):
		self.__putUrlParamsIntoParameters_();
		actualUrl = self.__url_.split("/");
		expectedUrl = urlTemplate.split("/");
		from lib import Util;

		for i in range(len(actualUrl)):
			actualPart = actualUrl[i];
			expectedPart = expectedUrl[i];
			if (expectedPart.find("") != -1):
				self.__parameters_[Util.substr(expectedPart, 1, len(expectedPart) - 2)] = actualPart;
			

	def getAppId(self):
		if (self.__parameters_.has_key(RestRequestItem.APP_ID)  and  self.__parameters_[RestRequestItem.APP_ID] == RestRequestItem.APP_SUBSTITUTION_TOKEN):
			return self.__token_.getAppId();
		elif (self.__parameters_.has_key(RestRequestItem.APP_ID)):
			return self.__parameters_[RestRequestItem.APP_ID];
		else:
			return 0;
		
	def getUser(self):
		from src.social.spi import UserId;
		return UserId.fromJson(self.__parameters_[RestRequestItem.USER_ID]) if self.__parameters_.has_key(RestRequestItem.USER_ID) else False;
	
	def getGroup(self):
		from src.social.spi import GroupId;
		return GroupId.fromJson(self.__parameters_[RestRequestItem.GROUP_ID]) if self.__parameters_.has_key(RestRequestItem.GROUP_ID) else False;
	
	def getStartIndex(self):
		if  RestRequestItem.START_INDEX in (self.__parameters_).keys() :
			if (len(self.__parameters_[RestRequestItem.START_INDEX])):
				try:
					return int(self.__parameters_[RestRequestItem.START_INDEX]);
				except:
					return self.__parameters_[RestRequestItem.START_INDEX];
			else:
				return RestRequestItem.DEFAULT_START_INDEX;
		

	def getCount(self):
		if  RestRequestItem.COUNT in (self.__parameters_).keys() :
			if (len(self.__parameters_[RestRequestItem.COUNT])):
				try:
					return int(self.__parameters_[RestRequestItem.COUNT]);
				except:
					return self.__parameters_[RestRequestItem.COUNT];
			else:
				return RestRequestItem.DEFAULT_COUNT;
			
	

	def getSortBy(self):
		if  RestRequestItem.SORT_BY in (self.__parameters_).keys() :
			if (not len(self.__parameters_[RestRequestItem.SORT_BY])):
				return self.__parameters_[RestRequestItem.SORT_BY];
			
		return None;
	

	def getSortOrder(self):
		if  RestRequestItem.SORT_ORDER in (self.__parameters_).keys() :
			if (not len(self.__parameters_[RestRequestItem.SORT_ORDER])): 
				return self.__parameters_[RestRequestItem.SORT_ORDER];
			
		return RestRequestItem.DEFAULT_SORT_ORDER;
	

	def getNetworkDistance(self):
		if  RestRequestItem.NETWORK_DISTANCE in (self.__parameters_).keys() : 
			if (not len(self.__parameters_[RestRequestItem.NETWORK_DISTANCE])): 
				try:
					return int(self.__parameters_[RestRequestItem.NETWORK_DISTANCE]);
				except:
					return self.__parameters_[RestRequestItem.NETWORK_DISTANCE];
		
		return False;
	

	def getFilterBy(self):
	
		if (not len(self.__parameters_[RestRequestItem.FILTER_BY])): 
			return self.__parameters_[RestRequestItem.FILTER_BY];
		
		return None;
	

	def getFilterOperation(self):
		if  RestRequestItem.FILTER_OP in (self.__parameters_).keys() :
			if (not len(self.__parameters_[RestRequestItem.FILTER_OP])): 
				return self.__parameters_[RestRequestItem.FILTER_OP];
			
			return None;
		

	def getFilterValue(self):
		if  RestRequestItem.FILTER_VALUE in (self.__parameters_).keys() :
			if (not len(self.__parameters_[RestRequestItem.FILTER_VALUE])): 
				return self.__parameters_[RestRequestItem.FILTER_VALUE];
		
		return None;
	

	def getUpdatedSince(self):
		if  RestRequestItem.UPDATED_SINCE in (self.__parameters_).keys() :
			if (not len(self.__parameters_[RestRequestItem.UPDATED_SINCE])): 
				return self.__parameters_[RestRequestItem.UPDATED_SINCE];
			
		return None;
	

	def getFields(self):
		defaultValue = {};
		return self.getFieldsWithDefaultValue(defaultValue);
	

	def getFieldsWithDefaultValue(self, defaultValue):
		if  RestRequestItem.FIELDS in (self.__parameters_).keys() : 
			if (len(self.__parameters_[RestRequestItem.FIELDS])): 
				paramValue = self.__parameters_[RestRequestItem.FIELDS];
				fieldNames = paramValue.split(',');
				fields = {};
				for fieldName in fieldNames:
					fields[fieldName] = 1;
				
				return fields;

		return defaultValue;
		
	

	def getPostData(self):
	
		return self.__postData_;
	

	def getUrl(self):
	
		return self.__url_;
	

	def setUrl(self, url):
	
		self.__url_ = url;
	

	def getMethod(self):
	
		return self.__method_;
	

	def setMethod(self, method):
	
		self.__method_ = method;
	

	def getParameters(self):
	
		return self.__parameters_;
	

	def setParameters(self, parameters):
		self.__parameters_ = parameters;
	

	def getToken(self):
	
		return self.__token_;
	

	def setToken(self, token):
	
		self.__token_ = token;
	

